package com.dycong.common.database.redis;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.BinaryJedis;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import redis.clients.util.Pool;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author zhaowen_zhuang
 */
public class JedisTemplate {

    private Pool<Jedis> pool;

    private static final Logger LOGGER = LoggerFactory.getLogger(JedisTemplate.class);

    public String get(final String key) {
        return this.execute(new JedisAction<String>() {
            @Override
            public String execute(Jedis jedis) {
                return jedis.get(key);
            }
        });
    }

    public byte[] get(final byte[] key) {
        return this.execute(new JedisAction<byte[]>() {
            @Override
            public byte[] execute(Jedis jedis) {
                return jedis.get(key);
            }
        });
    }

    public String set(final String key, final String value) {
        return this.execute(new JedisAction<String>() {
            @Override
            public String execute(Jedis jedis) {
                return jedis.set(key, value);
            }
        });
    }

    public Long setnx(final String key, final String value) {
        return this.execute(jedis -> jedis.setnx(key, value));
    }

    public String set(final byte[] key, final byte[] value) {
        return this.execute(new JedisAction<String>() {
            @Override
            public String execute(Jedis jedis) {
                return jedis.set(key, value);
            }
        });
    }

    public String setex(final String key, final int seconds, final String value) {
        return this.execute(new JedisAction<String>() {
            @Override
            public String execute(Jedis jedis) {
                return jedis.setex(key, seconds, value);
            }
        });
    }

    public String setex(final byte[] key, final int seconds, final byte[] value) {
        return this.execute(new JedisAction<String>() {
            @Override
            public String execute(Jedis jedis) {
                return jedis.setex(key, seconds, value);
            }
        });
    }

    public Set<String> keys(final String pattern) {
        return this.execute(new JedisAction<Set<String>>() {
            @Override
            public Set<String> execute(Jedis jedis) {
                return jedis.keys(pattern);
            }
        });
    }

    public Long expire(final String key, final int seconds) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.expire(key, seconds);
            }
        });
    }

    public Long expireAt(final String key, final long unixTime) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.expireAt(key, unixTime);
            }
        });
    }

    public Long persist(final String key) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.persist(key);
            }
        });
    }

    public Long pexpire(final String key, final long milliseconds) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.pexpire(key, milliseconds);
            }
        });
    }

    public Long pexpireAt(final String key, final long millisecondsTimestamp) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.pexpireAt(key, millisecondsTimestamp);
            }
        });
    }

    public Long pttl(final String key) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.pttl(key);
            }
        });
    }

    public Long ttl(final String key) {
        return this.execute(jedis -> jedis.ttl(key));
    }

    public String watch(final String key) {
        return this.execute(jedis -> jedis.watch(key));
    }

    public String unwatch() {
        return this.execute(BinaryJedis::unwatch);
    }

    public Transaction multi() {
        return this.execute(BinaryJedis::multi);
    }

    public String rename(final String oldkey, final String newkey) {
        return this.execute(new JedisAction<String>() {
            @Override
            public String execute(Jedis jedis) {
                return jedis.rename(oldkey, newkey);
            }
        });
    }

    public Long renamenx(final String oldkey, final String newkey) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.renamenx(oldkey, newkey);
            }
        });
    }

    public Long del(final String key) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.del(key);
            }
        });
    }

    public Long del(final byte[] key) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.del(key);
            }
        });
    }

    public Boolean exists(final String key) {
        return this.execute(new JedisAction<Boolean>() {
            @Override
            public Boolean execute(Jedis jedis) {
                return jedis.exists(key);
            }
        });
    }

    public Long hset(final String key, final String field, final String value) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.hset(key, field, value);
            }
        });
    }

    public Long hsetnx(final String key, final String field, final String value) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.hsetnx(key, field, value);
            }
        });
    }

    public String hmset(final String key, final Map<String, String> hash) {
        return this.execute(new JedisAction<String>() {
            @Override
            public String execute(Jedis jedis) {
                return jedis.hmset(key, hash);
            }
        });
    }

    public Map<String, String> hgetAll(final String key) {
        return this.execute(new JedisAction<Map<String, String>>() {
            @Override
            public Map<String, String> execute(Jedis jedis) {
                return jedis.hgetAll(key);
            }
        });
    }

    public String hget(final String key, final String field) {
        return this.execute(new JedisAction<String>() {
            @Override
            public String execute(Jedis jedis) {
                return jedis.hget(key, field);
            }
        });
    }

    public List<String> hmget(final String key, final String... fields) {
        return this.execute(new JedisAction<List<String>>() {
            @Override
            public List<String> execute(Jedis jedis) {
                return jedis.hmget(key, fields);
            }
        });
    }

    public Long hdel(final String key, final String... fields) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.hdel(key, fields);
            }
        });
    }

    public Long llen(final String key) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.llen(key);
            }
        });
    }

    public Long lpush(final String key, final String... values) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.lpush(key, values);
            }
        });
    }

    public Long rPush(final String key, final String... values) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.rpush(key, values);
            }
        });
    }
    
    public String lpop(final String key) {
        return this.execute(new JedisAction<String>() {
            @Override
            public String execute(Jedis jedis) {
                return jedis.lpop(key);
            }
        });
    }
    
    public String rpop(final String key) {
        return this.execute(new JedisAction<String>() {
            @Override
            public String execute(Jedis jedis) {
                return jedis.rpop(key);
            }
        });
    }

    public List<String> lrange(final String key, final long start, final long end) {
        return this.execute(new JedisAction<List<String>>() {
            @Override
            public List<String> execute(Jedis jedis) {
                return jedis.lrange(key,start,end);
            }
        });
    }

    public Boolean hexists(final String key , final String value) {
        return this.execute(new JedisAction<Boolean>() {
            @Override
            public Boolean execute(Jedis jedis) {
                return jedis.hexists(key, value);
            }
        });
    }

    public String lindex(final String key,final Long index) {
        return this.execute(new JedisAction<String>() {
            @Override
            public String execute(Jedis jedis) {
                return jedis.lindex(key, index);
            }
        });
    }

    public String lset(final String key, final Long index, final String value) {
        return this.execute(new JedisAction<String>() {
            @Override
            public String execute(Jedis jedis) {
                return jedis.lset(key, index, value);
            }
        });
    }

    public Long lrem(final String key, final Long count, final String value) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.lrem(key, count, value);
            }
        });
    }

    public Long zadd(final String key, final double score, final String member) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.zadd(key, score, member);
            }
        });
    }
    
    public Long zrem(final String key, final String... members) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.zrem(key, members);
            }
        });
    }


    public Long sadd(final String key, final String... members) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.sadd(key, members);
            }
        });
    }

    public Long srem(final String key, final String... members) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.srem(key, members);
            }
        });
    }


    public Long incr(final String key, final long value) {
        return this.execute(new JedisAction<Long>() {
            @Override
            public Long execute(Jedis jedis) {
                return jedis.incrBy(key,value);
            }
        });
    }

    public Set<String> smembers(final String key) {
        return this.execute(new JedisAction<Set<String>>() {
            @Override
            public Set<String> execute(Jedis jedis) {
                return jedis.smembers(key);
            }
        });
    }

    public Set<String> zrangeByScore(final String key, final double min, final double max, final int offset, final int count) {
        return this.execute(new JedisAction<Set<String>>() {
            @Override
            public Set<String> execute(Jedis jedis) {
                return jedis.zrangeByScore(key, min, max, offset, count);
            }
        });
    }

    public <T> T execute(JedisAction<T> action) {
        Jedis jedis = pool.getResource();
        try {
            return action.execute(jedis);
        } finally {
            jedis.close();
        }
    }

    public Pool<Jedis> getPool() {
        return pool;
    }

    public void setPool(Pool<Jedis> pool) {
        this.pool = pool;
    }

}
